from constants import *
import pygame,random,time

#####################################################
#                                                   #
#  Task class:                                      # 
#                                                   #
#  The Task class is one of the most important      #
#  classes, since it represents the actual tasks    #
#  or processes involved in most OS algorithms.     #
#                                                   #
#  A task can be dragged by mouse click and can be  #
#  added to CPUs, Queues or Lists.                  #
#                                                   # 
#  Author: Felix Rohrer                             #     
#  Course: CS MET 575                               #
#  Year:   2011                                     #
#                                                   #
#####################################################

class Task:

  # this static variable keeps count of the
  # total number of tasks initialized
  TASK_COUNT = 0

  @classmethod
  def updateTaskID(cls):
    cls.TASK_COUNT+=1
    return cls.TASK_COUNT

  def __init__(self, name, drawAreaDim=(DRAWAREA_WIDTH,DRAWAREA_HEIGHT), pos=(30,100), size=(30,30), textColor=(0,0,0)):
    # initialize pygame
    pygame.init()

    # set Task variables
    self.taskNr = Task.updateTaskID() # each task has unique number
    self.name = name
    self.drawArea = drawAreaDim
    self.pos = [pos[0]+size[0]*self.taskNr,pos[1]]

    # this offset defines, at what position the task got
    # dragged by a mouse-click
    self.posOffset = [None, None]

    self.size = size
    self.textColor = textColor
    self.bgColor = TASK_BG_COLOR

    # currentDS is the data structure this task belongs to
    self.currentDS = None

    # the execution time defines, how long this task
    # will execute in an executable unit such as a CPU
    self.execTime = TASK_EXEC_TIME
    self.curExecTime = self.execTime
    self.div = self.execTime/(self.size[0]-6)

    # defines how long a task pauses in a given data structure
    self.endWaitTime = 50
    self.curEndWaitTime = 50

    self.condition = 'NOSPECIAL'

    # create task container
    self.container = pygame.Surface(self.size)

    # define font
    self.textFont = pygame.font.SysFont(SYS_FONT,SYS_FONT_SIZE)

  def display(self, canvas,updateExecTime=True):

    # change bg color according to some action
    if self.condition == 'DRAGGED':
      self.bgColor = TASK_BG_COLOR_SELECTED
    elif self.condition == 'NOMOVE':
      self.bgColor = TASK_BG_COLOR_NOMOVE
    else:
      self.bgColor = TASK_BG_COLOR

    # if this data structure is not executable, 
    # reload execution bar
    if updateExecTime:
      if self.currentDS is not None:
	if not self.currentDS.isExecutionUnit() and self.curExecTime < self.execTime:
	  self.curExecTime += 1
	if self.currentDS.isExecutionUnit() and self.curExecTime > 0:
	  self.curExecTime -= 1

    # draw this task
    self.container.fill(self.bgColor)
    pygame.draw.rect(self.container, self.textColor, (0,0,self.size[0],self.size[1]), 1)

    # draw execution bar
    pygame.draw.rect(self.container, (255,0,0), (2,2,self.size[0]-4,7), 0)
    pygame.draw.rect(self.container, (0,255,0), (2,2,int(self.curExecTime/self.div),7), 0)
    pygame.draw.rect(self.container, (0,0,0), (2,2,self.size[0]-4,7), 1)

    # draw name into task
    self.container.blit(self.textFont.render(self.name, False, (0,0,0)),(2, self.size[1]/3))

    # print to main simulation area
    canvas.blit(self.container, (self.pos))

  # set new position of this task
  # make sure task stays within the drawing area
  # pos:          New position the current task should be moved to
  # dragged:      True, if user actually drags the task
  # ignoreNoMove: If true, task will still be moved even if the condition
  #               is NOMOVE (needed when data structures are moved around)

  def setPos(self, pos, dragged = False, ignoreNoMove = False):
    if self.condition is not 'NOMOVE' or ignoreNoMove:

      # save the offset of where the user clicked on the task
      # relative to the tasks left upper corner (self.pos) 
      if self.posOffset[0] is None and dragged:
        self.posOffset[0] = pos[0] - self.pos[0]
        self.posOffset[1] = pos[1] - self.pos[1]

      # set new x coordinate of this task
      if pos[0] < self.size[0]/2:
	self.pos[0] = 1
      elif pos[0] > (self.drawArea[0] - self.size[0]/2):
	self.pos[0] = self.drawArea[0] - self.size[0] - 1
      else:
	if dragged:
          # set position according to the offset saved
          # when the task became dragged
          self.pos[0] = pos[0] - self.posOffset[0]
	else:
	  self.pos[0] = pos[0]

      # find new y coordinate of this task
      if pos[1] < self.size[1]/2:
	self.pos[1] = 1
      elif pos[1] > (self.drawArea[1] - self.size[1]/2):
	self.pos[1] = self.drawArea[1] - self.size[1] - 1
      else:
	if dragged:
          # set position according to the offset saved
          # when the task became dragged
          self.pos[1] = pos[1] - self.posOffset[1]
	else:
	  self.pos[1] = pos[1]

  # get current position of this task
  # if offset = True, return coordinate of center of this task
  def getPos(self,offset=False):
    if offset:
      return [self.pos[0]+self.size[0]/2,self.pos[1]+self.size[1]/2]
    return self.pos

  # execution time defines how long this task needs to execute
  def getExecTime(self):
    return self.curExecTime

  # reset execution time
  def resetExecTime(self):
    self.curExecTime = self.execTime

  # execution time defines how long this task needs to execute
  # returns True if execTime not yet 0
  def decEndTime(self):
    self.curEndWaitTime -= 1
    if self.curEndWaitTime <= 0:
      # restore end wait time
      self.curEndWaitTime = self.endWaitTime
      return False
    return True

  # set this tasks condition
  def setCondition(self, cond):
    self.condition = cond

  # get this tasks condition
  def getCondition(self):
    return self.condition

  # check if a user tries to drag this task (collision with mouse-click)
  def collision(self, pointer):
    if pointer[0] > self.pos[0] and pointer[0] < self.pos[0]+self.size[0] \
                           and pointer[1] > self.pos[1] and pointer[1] < self.pos[1]+self.size[1]:
      self.condition = 'DRAGGED'
      return True
    return False

  # return True, if this task is currently dragged
  def isDragged(self):
    if self.condition == 'DRAGGED':
      return True
    return False

  # make sure this task is not dragged anymore
  def dropped(self):
    self.condition = "NOSPECIAL"
    self.posOffset[0] = None
    self.posOffset[1] = None

  # get data structure to which this task currenty belongs
  def getDSName(self):
    if self.currentDS is None:
      return 'None'
    return self.currentDS.name

  # get data structure to which this task currenty belongs
  def getDSType(self):
    if self.currentDS is None:
      return 'None'
    return self.currentDS.DSType

  # set data structure that this task now belongs to
  def setDS(self,q):
    self.currentDS = q
